package com.ge.mythreed.service.impl;

import com.alibaba.fastjson.JSON;
import com.ge.mytestparent.utils.Sm4Utils;
import com.ge.mythreed.dto.req.EncryptReqDto;
import com.ge.mythreed.entity.EncryptMobile;
import com.ge.mythreed.entity.PmPromotionTargetlist;
import com.ge.mythreed.mapper.PmPromotionTargetlistMapper;
import com.ge.mythreed.service.EncryptDataService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author geqingxiang
 * @PackageName my-project
 * @Package com.ge.mythreed.service.impl
 * @Date 2024/1/25 10:11
 * @Version 1.0
 */
@Slf4j
@Service
public class EncryptDataServiceImpl implements EncryptDataService {

    @Autowired
    private PmPromotionTargetlistMapper pmPromotionTargetlistMapper;
    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor tradPoolTaskExecutor;

    @Override
    public String encryptMobiles(EncryptReqDto encryptReqDto) {
        long start = System.currentTimeMillis();
        long totalNumber = 0;
        int encryptNum = getEncryptNum(encryptReqDto);
        for (int i = 0; i < encryptNum; i++) {
            List<PmPromotionTargetlist> pmPromotionTargetlistList = pmPromotionTargetlistMapper.getUnencryptedData(encryptReqDto.getExecutionsNum());
            if (CollectionUtils.isNotEmpty(pmPromotionTargetlistList)) {
                List<List<PmPromotionTargetlist>> listList = splitList(pmPromotionTargetlistList, encryptReqDto.getUpdateNum());
                ArrayList<CompletableFuture<String>> completableFutures = new ArrayList<>();
                for (List<PmPromotionTargetlist> list : listList) {
                    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                        return updateEncryptMobile(list);
                    }, tradPoolTaskExecutor);
                    completableFutures.add(future);
                }
                CompletableFuture[] array = completableFutures.toArray(new CompletableFuture[]{});
                try {
                    long executorTimeOut = 120000L;
                    CompletableFuture.allOf(array).get(executorTimeOut, TimeUnit.MILLISECONDS);
                } catch (Exception e) {
                    log.error("异步加密手机号异常", e);
                }
                totalNumber += pmPromotionTargetlistList.size();
            } else {
                break;
            }
        }
        long takeTime = System.currentTimeMillis() - start;
        log.info("加密成功，加密总数：{},执行时长：{}", totalNumber, takeTime);
        return "加密成功，加密总数：" + totalNumber + ",执行时长：" + takeTime;
    }

    public String updateEncryptMobile(List<PmPromotionTargetlist> pmPromotionTargetlistList) {
        String currentThreadName = Thread.currentThread().getName();
        List<EncryptMobile> encryptMobileList = pmPromotionTargetlistList.stream().map(item -> {
            EncryptMobile encryptMobile = new EncryptMobile();
            encryptMobile.setPptlId(item.getPptlId());
            encryptMobile.setTargetCode(Sm4Utils.encrypt(item.getTargetCode()));
            encryptMobile.setOldTargetCode(item.getTargetCode());
            return encryptMobile;
        }).collect(Collectors.toList());
        log.info("线程：{},更新加密手机号数据：{}", currentThreadName, JSON.toJSONString(encryptMobileList));
        pmPromotionTargetlistMapper.updateEncryptMobile(encryptMobileList);
        return "success";
    }

    public int getEncryptNum(EncryptReqDto encryptReqDto) {
        if (encryptReqDto == null || encryptReqDto.getEncryptNum() == null) {
            int count = pmPromotionTargetlistMapper.getUnencryptedDataCount();
            return (count / encryptReqDto.getExecutionsNum()) + 1;
        }
        return encryptReqDto.getEncryptNum();
    }

    public static <T> List<List<T>> splitList(List<T> source, int length) {
        List<List<T>> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(source) || length <= 0) {
            return result;
        }
        Deque<T> deque = new LinkedList<>(source);
        while (!deque.isEmpty()) {
            int size = Math.min(length, deque.size());
            List<T> sublist = new ArrayList<>(size);
            for (int i = 0; i < size; i++) {
                sublist.add(deque.removeFirst());
            }
            result.add(sublist);
        }
        return result;
    }
}
